/* Version iterativa de lista con header implementadas dinamicamente */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../include/list.h"

/* este es el nodo distinguido header */

struct listCDT
{
	struct nodoCDT * nodos;
	cmpfnT Compara;
	freefnT Libera;
	size_t tam;
	int cant;
	struct nodoCDT * actual;
};

/* este es el formato de cada nodo de la lista con informacion */

typedef struct nodoCDT
{
	listElementT dato; /* donde se guarda la informacion */
	struct nodoCDT * tail;
}nodoCDT;

/* Funciones estaticas */
static void
Error(const char *cartel)
{
	printf("%s\n", cartel);
	exit(EXIT_FAILURE);
}

static nodoCDT *
ListTail(nodoCDT * listInterna)
{
	if (listInterna == NULL)
		Error("Acceso a la cola de una lista vacia\n");
	
	return listInterna->tail;
}

listADT
NewList(int (*fn) (listElementT, listElementT), void (*libera) (listElementT), size_t tam)
{
	listADT auxi;
	if (fn == NULL)
		return NULL;
	if (tam <= 0)
		return NULL;
	
	if ( (auxi = malloc(sizeof(struct listCDT))) == NULL )
		Error("No hay lugar disponible\n");
	
	auxi->nodos = NULL;
	auxi->Compara = fn;
	auxi->Libera = libera;
	auxi->tam = tam;
	auxi->actual = NULL;
	auxi->cant= 0;
	
	return auxi;
}

void
Insert(listADT list, listElementT element)
{
	nodoCDT *ant, *rec, *auxi;
	
	ant = rec = list->nodos;
	
	while (rec != NULL && (*list->Compara)(rec->dato,element) < 0)
	{
		ant = rec;
		rec = ListTail(rec);
	}
	
	/* ... como no se aceptan repetidos ... */
	if (rec != NULL && (*list->Compara)(rec->dato,element) == 0)
		return;
	
	
	/* se inserta */
	if ((auxi = malloc(sizeof(struct nodoCDT))) == NULL)
		Error("No hay lugar para otro nodo\n");
	
	auxi->tail = rec;
	
	/* genero lugar para copiar la info que recibo indirectamente */
	if ((auxi->dato = malloc(list->tam)) == NULL)
	{
		free(auxi);
		Error("No hay lugar para otro nodo\n");
	}
	
	memcpy(auxi->dato,element,list->tam);
	list->cant++;
	
	
	/* es el primero */
	if (ant == rec)
		list->nodos = auxi;
	else
		ant->tail = auxi;


	// para evitar tener que invocar SetBegin c/vez que tenga que
	// usar GetDato
	if (list->actual == NULL) {
		list->actual = auxi;
	}
}

int
Delete(listADT list, listElementT element)
{
	nodoCDT *ant, *rec;
	
	if(list == NULL)
		return 0;
	
	rec = ant = list->nodos;
	
	while (rec != NULL && (*list->Compara)(rec->dato,element) < 0)
	{
		ant = rec;
		rec = ListTail(rec);
	}
	
	/* no lo encontre */
	while (rec != NULL && (*list->Compara)(rec->dato,element) != 0)
		return 0;
	
	
	/* a borrarlo */
	if (ant == rec) /* es el primero */
		list->nodos = ant->tail;
	else                    /* es del medio o final */
		ant->tail = rec->tail;
	/* si el elemento a borrar era el indicado por actual debemos mover a actual
	* para que apunte a un elemento dentro de la lista y no uno borrado */
	if (rec == list->actual)
		list->actual = ListTail(list->actual);
	
	list->Libera(rec->dato);
	free(rec->dato);
	free(rec);
	list->cant --;
	
	return 1;
}

int
ListIsEmpty(listADT list)
{
	return (list == NULL || list->nodos == NULL);
}

int
ElementBelongsList(listADT list, listElementT element)
{
	nodoCDT *actual;
	void *dato;
	int rta;
	
	actual = list->actual;
	
	if ((dato = malloc(list->tam)) == NULL)
		Error("problemas con memoria\n");
	
	SetBegin(list);
	while( GetDato(list,dato) && (*list->Compara)(dato,element) <0 )
		;
	
	list->actual = actual;
	
	rta = dato != NULL && (*list->Compara)(dato,element) == 0;
	
	free(dato);
	
	return rta;
}

void
SetBegin(listADT list)
{
	if( list== NULL)
		return;
	list->actual = list->nodos;
}

void
FreeList(listADT list)
{
	nodoCDT *auxi;
	
	while (!ListIsEmpty(list))
	{
		auxi = list->nodos;
		list->nodos = auxi->tail;
		list->Libera(auxi->dato);
		//free(auxi->dato);
		free(auxi);
	}
	
	free(list);
}


int
GetDato(listADT list, listElementT element)
{
	if (list==NULL || list->actual == NULL)
		return 0;
	
	memcpy(element, list->actual->dato, list->tam);
	list->actual = ListTail(list->actual);
	
	return 1;
}


listElementT
GetDatoByComp(listADT list, listElementT element)
{
	listElementT answer, answer2;
	nodoCDT *actualnode;
	
	if(list==NULL || list->nodos==NULL)
		return NULL;
	
	if((answer2=malloc(list->tam))==NULL)
		return NULL;
	
	actualnode = list->nodos;
	while(actualnode!=NULL)
	{
		answer=actualnode->dato;
		if(list->Compara(answer, element)==0)
		{
			memcpy(answer2, actualnode->dato, list->tam);
			return answer2;
		}
		actualnode=actualnode->tail;
	}
	
	free(answer2);
	return NULL;
}

int
GetListCount(listADT list)
{
	if( list ==NULL)
		return 0;
	return list->cant;
}